using System;
using System.Collections.Generic;
using TheGameClient.Webservices;

namespace TheGameClient.Processes
{
	public class ProgramManager
	{
		private static ProgramManager _instance;

		private readonly Dictionary<Guid, RegisteredProcess> _processes;

		private ProgramManager()
		{
			_processes = new Dictionary<Guid, RegisteredProcess>();
		}

		public static ProgramManager Instance
		{
			get { return _instance ?? (_instance = new ProgramManager()); }
		}

		public IProcess GetProcessById(Guid processId)
		{
			RegisteredProcess p;

			if (!Instance._processes.TryGetValue(processId, out p))
				throw new InvalidOperationException("Process " + processId + " not found.");
			return p.ProcessBase;
		}

		public void RegisterProcess(IProgram processBase)
		{
			if (Instance._processes.ContainsKey(processBase.ProcessId))
				return;
			RegisteredProcess regProc = new RegisteredProcess {ProcessPriority = 1, ProcessBase = processBase};
			Instance._processes.Add(processBase.ProcessId, regProc);
		}

		public void UnregisterProcess(Guid processId)
		{
			if (Instance._processes.ContainsKey(processId))
				Instance._processes.Remove(processId);
		}

		public void SetProcessPriority(Guid processId, int priority)
		{
			if (priority < 0 || priority > 100)
				return;

			if (Instance._processes.ContainsKey(processId))
			{
				Instance._processes[processId].ProcessPriority = priority;
			}
		}

		public int GetProcessPriority(Guid processId)
		{
			if (Instance._processes.ContainsKey(processId))
				return Instance._processes[processId].ProcessPriority;
			throw new InvalidOperationException("Process " + processId + " not found.");
		}

		public event EventHandler<GetSpeedEventArgs> GetSpeedForProcessCompleted;

		public void GetSpeed(Guid processId)
		{
			RegisteredProcess regProc;
			if (!Instance._processes.TryGetValue(processId, out regProc))
				throw new InvalidOperationException("Process with id " + processId + " not found.");

			App.Client.GetProgramSpeedCompleted += ClientGetProgramSpeedCompleted;
			App.Client.GetProgramSpeedAsync(App.InternalSessionId, regProc.ProcessBase.ProgramId);
		}

		private void ClientGetProgramSpeedCompleted(object sender, GetProgramSpeedCompletedEventArgs e)
		{
			App.Client.GetProgramSpeedCompleted -= ClientGetProgramSpeedCompleted;
			if (e.Error != null)
			{
				App.ServerError();
				return;
			}
			if (GetSpeedForProcessCompleted != null)
				GetSpeedForProcessCompleted(this, new GetSpeedEventArgs {Speed = 1/e.Result});
		}

		#region async to sync - nonworking, not good practise.

		//private ManualResetEvent mre;
		//private double speed;
		//public double getSpeed(Guid processId)
		//{
		//    RegisteredProcess regProc;
		//    if (!Instance.processes.TryGetValue(processId, out regProc))
		//        throw new InvalidOperationException("Process with id " + processId + " not found.");
		//    mre = new ManualResetEvent(false);
		//    new Thread(new ThreadStart(delegate
		//    {
		//        Thread.Sleep(1000);
		//        App.Client.GetProgramSpeedCompleted += Client_GetProgramSpeedCompleted2;
		//        App.Client.GetProgramSpeedAsync(App.InternalSessionId, regProc.ProcessBase.ProgramId);
		//    }));
		//    if (!mre.WaitOne(10000))
		//        return -1;
		//    return speed;
		//}

		//void Client_GetProgramSpeedCompleted2(object sender, GetProgramSpeedCompletedEventArgs e)
		//{
		//    if (e.Error != null) { App.ServerError(); return; }
		//    speed = e.Result;
		//    mre.Set();
		//}

		#endregion
	}

	public class GetSpeedEventArgs : EventArgs
	{
		public double Speed { get; set; }
	}

	public enum SpeedType
	{
		Cpu,
		Bandwidth
	}

	public class RegisteredProcess
	{
		public int ProcessPriority { get; set; }
		public IProgram ProcessBase { get; set; }
	}
}